#include "memory/cache.h"
#include "common.h"
#include "burst.h"
#include "stdlib.h"
void cddr3_read(hwaddr_t addr, void *data);
void cddr3_write(hwaddr_t addr, void *data, uint8_t *mask);
void init_cache(){
    int i=0;
    for(;i<CACHE_SIZE/CACHE_BLOCK;i++){
        cache[i].valid=0;
        cache[i].tag=0;
        memset(cache[i].byte, 0, CACHE_BLOCK);
    }
    for (i = 0; i < CACHE_SIZE2 / CACHE_BLOCK; i++)
	{
		cache2[i].valid = false;
		cache2[i].dirty = false;
		cache2[i].tag = 0;
		memset(cache2[i].data, 0, CACHE_BLOCK);
	}
    cnt=0;
}

uint32_t cache_read(hwaddr_t addr){
    uint8_t s = (addr>>6)&0x7f;
    bool hit = false;
    int i;
    for(i=s*E;i<s*E+E;i++){
        if(cache[i].valid && cache[i].tag==addr>>13){
            hit = true;
            cnt+=2;
            break;     
        }
    }
    if(!hit){	//找不到
        for(i=s*E;i<s*E+E;i++){
            if(cache[i].valid==0){
                break;
            }
        }
        if(i==s*E+E){
            srand(0);
            i = s*E+rand()%E;
        }
        cache[i].valid=1;
        cache[i].tag=addr>>13;
        int j=cache_read2(addr);
        memcpy(cache[i].byte, cache2[j].data, CACHE_BLOCK);
    }
    return i;
}

uint32_t cache_read2(hwaddr_t addr){
	uint32_t s = (addr >> 6) & ((1 << 12) - 1);
	uint32_t block = (addr >> 6) << 6;
	int i;
	bool hit = false;
	for (i = s * E2; i < (s + 1) * E2; i++)
	{
		if (cache2[i].tag == (addr >> 18) && cache2[i].valid)
		{
			hit = true;
			cnt+=2;
			break;
		}
	}
	if (!hit)
	{
		int j;
		for (i = s * E2; i < (s + 1) * E2; i++)
		{
			if (!cache2[i].valid)
				break;
		}
		if (i == (s + 1) * E2) 
		{
			srand(0);
			i = s * E2 + rand() % E2;
			if (cache2[i].dirty)
			{
				uint8_t mask[BURST_LEN * 2];
				memset(mask, 1, BURST_LEN * 2);
				for (j = 0; j < CACHE_BLOCK / BURST_LEN; j++)
					cddr3_write(block + j * BURST_LEN, cache2[i].data + j * BURST_LEN, mask);
			}
		}
		cache2[i].valid = true;
		cache2[i].tag = addr >> 18;
		cache2[i].dirty = false;
		for (j = 0; j < BURST_LEN; j++){
			    cddr3_read(block + j * BURST_LEN, cache2[i].data + j * BURST_LEN);
        }
		cnt += 200; 
	}
	return i;
}

void cache_write2(hwaddr_t addr, size_t len, uint32_t data, bool * success)
{
	uint32_t s = (addr >> 6) & ((1 << 12) - 1); 
	uint32_t offset = addr&0x3f;	
	int i;
	bool hit = false;
	for (i = s * E2; i < (s + 1) * E2; i++)
	{
		if (cache2[i].tag == (addr >> 13) && cache2[i].valid)
		{
			hit = true;
			break;
		}
	}
	if (!hit){
		i = cache_read2(addr);
        *success = true;
    }
	cache2[i].dirty = true;
	memcpy(cache2[i].data + offset, &data, len);
}

void cache_write(hwaddr_t addr, size_t len, uint32_t data,bool * success){
    uint8_t s = (addr>>6)&0x7f;
    uint32_t offset = addr&0x3f;
    bool hit = false;
    int i;
    for(i=s*E;i<s*E+E;i++){
        if(cache[i].valid && cache[i].tag==addr>>13){
            hit = true;
            break;     
        }
    }
    if(hit){	// 写直通
        memcpy(cache[i].byte + offset, &data, len);
    }
    cache_write2(addr, len, data, success);
}